#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <stdbool.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>


#define CMD_MAX_LEN 1024
#define ARGS_MAX_LEN 64
#define ARGS_SEP " "

int SplitArgv(char* commandstr, char* argv[])
{
    argv[0] = strtok(commandstr, ARGS_SEP);
    if(argv[0] == NULL)
        return -1;
    int i = 1;
    while(argv[i++] = strtok(NULL, ARGS_SEP));
    
    return 0;
}

void PrintArgv(char* argv[])
{
    for(int i = 0; argv[i]; ++i)
    {
        printf("%d:%s\n", i, argv[i]);
    }
}

int main()
{
    char commandstr[CMD_MAX_LEN] = { '\0' };
    char* argv[ARGS_MAX_LEN] = { NULL };
    extern char** environ;
    int last_exit_code = 0; 
    
    while(true)
    {
        char* workDir = getenv("PWD");
        if(strcmp(workDir, getenv("HOME")) == 0)
        {
            workDir = "~";
        }
        else if(strcmp(workDir, "/") != 0)
        {
            workDir += strlen(workDir) - 1;
            while(*(workDir-1) != '/')
            {
                workDir--;
            }
        }
        printf("[%s@%s %s]# ", getenv("USER"), getenv("HOSTNAME"), workDir);
        fflush(stdout);
        if(fgets(commandstr, sizeof(commandstr) - 1,stdin))
        {
            //delete '\n',and set '\0'
            commandstr[strlen(commandstr) - 1] = '\0';
            //printf("%s\n",commandstr);
            if(SplitArgv(commandstr, argv) == -1)
                continue;

            //PrintArgv(argv);
            
            //内建命令
            //cd
            if(strcmp(argv[0], "cd") == 0)
            {
                if(argv[1] != NULL)
                {
                    if(strcmp(argv[1], "-") == 0)
                    {
                        char* oldpwd = getenv("OLDPWD");
                        if(oldpwd == NULL)
                        {
                            printf("unset OLDPWD\n");
                        }
                        else
                        {
                            if(chdir(oldpwd) == -1)
                            {
                                printf("-mybash cd: %s: No such directory\n", oldpwd);
                                last_exit_code = 1;
                                continue;
                            }
                            setenv("OLDPWD", getenv("PWD"), true);
                            setenv("PWD", oldpwd, true);
                        }
                    }
                    else if(strcmp(argv[1], "~") == 0)
                    {
                        if(chdir(getenv("HOME")) == -1)
                        {
                            printf("-mybash cd: %s: No such directory\n", getenv("HOME"));
                            last_exit_code = 1;
                            continue;
                        }
                        setenv("OLDPWD", getenv("PWD"), true);
                        setenv("PWD", getenv("HOME"), true);
                    }
                    else
                    {
                        char* pwd = realpath(argv[1], NULL);
                        if(chdir(argv[1]) == -1)
                        {
                            printf("-mybash cd: %s: No such directory\n", argv[1]);
                            last_exit_code = 1;
                            free(pwd);
                            continue;
                        }
                        //printf("realpath:%s\n",pwd);
                        setenv("OLDPWD", getenv("PWD"), true);
                        setenv("PWD", pwd , true);
                        free(pwd);
                    }
                }
                else
                {
                    if(chdir(getenv("HOME")) == -1)
                    {
                        printf("-mybash cd: %s: No such directory\n", getenv("HOME"));
                        last_exit_code = 1;
                        continue;
                    }
                    setenv("OLDPWD", getenv("PWD"), true);
                    setenv("PWD", getenv("HOME"), true);
                }
                last_exit_code = 0;
                continue;
            }
            //echo
            if(strcmp(argv[0], "echo") == 0)
            {
                char exit_code_buffer[5] = { '\0' };
                for(int i = 1; argv[i]; ++i)
                {
                    char* tmp = argv[i];
                    if(argv[i][0] == '$')
                    {
                        if(argv[i][1] == '?')
                        {
                            snprintf(exit_code_buffer, sizeof(exit_code_buffer), "%d", last_exit_code);
                            tmp = exit_code_buffer;
                        }
                        else
                        {
                            tmp = getenv(argv[i] + 1);
                        }
                    }
                    else if(strcmp(argv[i], "~") == 0)
                    {
                        tmp = getenv("HOME");
                    }
                    if(tmp)
                        printf("%s ", tmp);
                }
                printf("\n");
                last_exit_code = 0;
                continue;
            }
            //env
            if(strcmp(argv[0], "env") == 0)
            {
                for(int i = 0; environ[i]; ++i)
                {
                    printf("%s\n",environ[i]);
                }
                last_exit_code = 0;
                continue;
            }
            //export
            if(strcmp(argv[0], "export") == 0)
            {
                if(argv[1] != NULL)
                {
                    char* key = argv[1];
                    char* equal = strchr(argv[1], '=');
                    if(equal == NULL)
                    {
                        printf("\"export key\" is not currently supported, please use \"export key=value\"\n");
                    }
                    else
                    {
                        *equal = '\0';
                        char* value = equal + 1;
                        setenv(key, value, true);
                    }
                }
                last_exit_code = 0;
                continue;
            }
            
           

            //命令别名，使用自动配色方案
            if(strcmp(argv[0], "ls") == 0
            || strcmp(argv[0], "grep") == 0)
            {
                printf("alias\n");
                int i = 0;
                while(argv[i]) i++;
                argv[i] = "--color=auto";
                argv[i+1] = NULL;
            }


            pid_t pid = fork();
            if(pid < 0)
            {
                printf("can't create process\n");
                continue;
            }

            if(pid == 0)
            {
                execvp(argv[0], argv);
                printf("-mybash: %s: command not found\n",argv[0]);
                exit(127);
            }
            int status = 0;
            pid_t ret = waitpid(pid, &status, 0);
            if(ret > 0)
            {
                last_exit_code = WEXITSTATUS(status);
                //if(WIFEXITED(status))
                //{
                //    last_exit_code = WEXITSTATUS(status);
                //}
            }
        }
        else
        {
            //perror("error occur at fgets or encounter the EOF\n");
            //空白命令行，Ctrl+D会退出bash
            printf("\n");
            fflush(stdout);
            return 0;
        }
    }
    return 0;
}
